import { defineComponent, onMounted, ref, PropType } from 'vue'
import * as Three from 'three'

export default defineComponent({
  name: 'Particle',

  props: {
    colors: { // 粒子颜色
      type: Array as PropType<string[]>,
      default: () => ['#C59D53', '#F0CC88', '#996D1D']
    },
    particleCount: { // 粒子个数
      type: Number,
      default: 180
    },
    initialRadius: { // 初始半径
      type: Number,
      default: 0.1
    },
    movementSpeed: { // 移动速度
      type: Number,
      default: 2.2
    }
  },

  setup(props) {
    let renderer: Three.WebGLRenderer; let scene: Three.Scene; let
      camera: Three.PerspectiveCamera

    const systemCount = ref(1)
    const directions = ref<{x: number; y: number; z: number;}[]>([])
    const starSystems = ref<Three.Mesh[]>([])

    const renderFun = () => {
      renderer.render(scene, camera)
    }

    const getPastelColor = (): string => props.colors[systemCount.value - 1]

    const resize = () => {
      const canvas = document.getElementById('canvas')
      if (canvas) {
        const { offsetWidth, offsetHeight } = canvas
        camera.aspect = offsetWidth / offsetHeight
        camera.updateProjectionMatrix()
        renderer.setSize(offsetWidth, offsetHeight)
        renderFun()
      }
    }

    const setStage = () => {
      const canvas = document.getElementById('canvas')
      if (canvas) {
        const { offsetWidth, offsetHeight } = canvas
        renderer = new Three.WebGLRenderer({ canvas: canvas as HTMLCanvasElement, antialias: true })
        renderer.setPixelRatio(window.devicePixelRatio)
        renderer.autoClear = false
        renderer.setSize(offsetWidth, offsetHeight)
        scene = new Three.Scene()
        camera = new Three.PerspectiveCamera(45, offsetWidth / offsetHeight, 1, 500)
        camera.position.z = 50
        window.addEventListener('resize', resize, false)
      }
    }

    const addStars = (color: string, x: number, y: number) => {
      const geometry = new Three.CircleBufferGeometry(0.6, 6)
      const materials = new Three.MeshBasicMaterial({
        color,
        transparent: false,
        blending: Three.AdditiveBlending,
        depthTest: false
      })
      const vertex = new Three.Vector3()
      let i
      let k
      const count = props.particleCount
      for (i = 0, k = 0; count >= 0 ? k < count : k > count; i = count >= 0 ? k += 3 : k -= 3) {
        vertex.x = x
        vertex.y = y
        vertex.z = 0
        const starSystem = new Three.Mesh(geometry, materials)
        directions.value.push({
          x: (Math.random() * props.movementSpeed) - (props.movementSpeed / 2),
          y: (Math.random() * props.movementSpeed) - (props.movementSpeed / 2),
          z: (Math.random() * props.movementSpeed) - (props.movementSpeed / 2)
        })
        starSystems.value.push(starSystem)
        scene.add(starSystem)
      }
    }

    const animate = () => {
      let particle
      for (let i = 0; i < starSystems.value.length; i++) {
        particle = starSystems.value[i].position
        particle.x += directions.value[i].x
        particle.y += directions.value[i].y
        particle.z += directions.value[i].z
      }
      renderFun()
      requestAnimationFrame(animate)
    }

    onMounted(() => {
      setStage()
      setInterval(() => {
        if (systemCount.value < props.colors.length) {
          systemCount.value += 1
          return addStars(getPastelColor(), 0, 0)
        }
      }, 100)
      animate()
      renderFun()
    })

    return () => (<canvas id="canvas" style="width: 100%; height: 100%" />)
  }
})
